/**
 *******************************************************************************
 * @FileName  : pan108x_rcc.c
 * @Author    : GaoQiu
 * @CreateDate: 2023-08-01
 * @Copyright : Copyright(C) Panchip
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between Panchip and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include "pan10x_rcc.h"
#include "platform/platform.h"
#include "stack/controller/bb_api.h"

#include "utils/utils.h"
#include "utils/track.h"

#if defined(IP_108)
void RCC_Wait3VSync(void)
{
	ANA->LP_REG_SYNC |= ANAC_LP_REG_SYNC_3V_Msk;
	while(ANA->LP_REG_SYNC & (ANAC_LP_REG_SYNC_3V_Msk | ANAC_LP_REG_SYNC_STS_Msk));
}

void RCC_XTHPowerOn(void)
{
	/* Enable fsyn_digclk / Enable startup fast */
	CLK->XTH_CTRL |= BIT(2)|BIT(0);

	/* Power on XTH */
	CLK->CLK_TOP_CTRL |= BIT(3);

	/* Wait XTH stable */
	while(!(CLK->XTH_CTRL & BIT(24))){}
}

void RCC_RCHPowerOn(void)
{
	/* Power on RCH */
	CLK->CLK_TOP_CTRL |= BIT(1);

	/* Wait RCH stable */
	while(!(CLK->RCH_CTRL & BIT(24))){}
}

void RCC_PLLPowerOn(uint8_t PLLClkSrc, uint8_t PLLOutFreq)
{
	uint32_t pllReg = CLK->DPLL_CTRL;
	pllReg &= ~(BIT(1)|BIT(0));
	CLK->DPLL_CTRL = pllReg | (PLLClkSrc << 1) | (PLLOutFreq << 0);

	/* Power on PLL */
	CLK->CLK_TOP_CTRL |= BIT(4);

	/* Wait PLL stable */
	while(!(CLK->DPLL_CTRL & BIT(24))){}
}

void RCC_RCLPowerOn(void)
{
	/* Power on RCL */
	CLK->CLK_TOP_CTRL |= BIT(0);

	/* Wait RCL stable */
	while(!(CLK->RCL_CTRL & BIT(24))){}
}

void RCC_XTLPowerOn(void)
{
	/* Power on XTL */
	CLK->CLK_TOP_CTRL |= BIT(2);

	/* Wait XTL stable */
	while(!(CLK->XTL_CTRL & BIT(24))){}
}

void RCC_SetSysClkSrc(uint8_t sysClkSrc)
{
	uint32_t reg = CLK->CLK_TOP_CTRL;
	reg &= ~(3ul<<8);
	CLK->CLK_TOP_CTRL = reg|(sysClkSrc<<8);
}

void RCC_Set32kClkSrc(uint8_t clk32kSrc)
{
	if(RCC_32kClkSrc_RCL == clk32kSrc){
		CLK->CLK_TOP_CTRL &= ~BIT(10);
	}
	else{
		CLK->CLK_TOP_CTRL |= BIT(10);
	}
}

void SysClkInit(void)
{
#if CFG_DCDC_ENABLE
	PWR_SetDcdcModeEnable(CFG_DCDC_ENABLE);
#endif

    /* Unlock protected registers */
    SYS_UnlockReg();

    GPIO->DBCTL &= ~BIT31; //disable GPIO retention

    ANA->LP_FSYN_LDO |= 1;
    ANA->LP_LP_LDO = 0x2a; //Set LPLDO  [!!!]important

    /* Power On XTH */
    RCC_XTHPowerOn();

    /* Power On PLL */
#if CFG_SYS_CLK_MHZ == 64 || CFG_SYS_CLK_MHZ == 32
    RCC_PLLPowerOn(RCC_PLLClkSrc_XTH, RCC_PLLOutFreq_64M);
#elif CFG_SYS_CLK_MHZ == 48
    RCC_PLLPowerOn(RCC_PLLClkSrc_XTH, RCC_PLLOutFreq_48M);
#endif

    /* Power On XTL */
    RCC_XTLPowerOn();

    //RCC_RCLPowerOn();

    //RF_EnableLDO();

#if 1
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_ROM | CLK_AHBPeriph_EFUSE | CLK_AHBPeriph_GPIO, DISABLE);
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_APB1 | CLK_AHBPeriph_APB2|CLK_AHBPeriph_BLE_32K|CLK_AHBPeriph_BLE_32M, ENABLE);

    /* save power */
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_All, DISABLE);
    CLK_APB2PeriphClockCmd(CLK_APB2Periph_All, DISABLE);
#else
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_All,   ENABLE);
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_All, ENABLE);
    CLK_APB2PeriphClockCmd(CLK_APB2Periph_All, ENABLE);
#endif

    CLK_PCLK1Config(0);
    CLK_PCLK2Config(0);

    /* Select system clock source */
#if CFG_SYS_CLK_MHZ == 64 ||CFG_SYS_CLK_MHZ == 48
    RCC_SetSysClkSrc(RCC_SysClkSrc_PLL);
#elif CFG_SYS_CLK_MHZ == 32
    CLK_HCLKConfig(1); //64M/2 = 32M
    RCC_SetSysClkSrc(RCC_SysClkSrc_PLL);
#else //default
    RCC_SetSysClkSrc(RCC_SysClkSrc_PLL);
#endif
    SystemCoreClockUpdate();

	/* Select 32K clock source */
    // Active 32K setting
    if(CFG_SLEEP_CLOCK_SOURCE == 2){
		CLK->ACT_32K_CTRL |= 0x02; //select 32k clock from 32M XTH
		CLK->ACT_32K_CTRL |= 0x01; //Enable active 32k clock
    }
	// 32K XTAL/RCL setting
	else{
		RCC_Set32kClkSrc(CFG_SLEEP_CLOCK_SOURCE);
	}

    /* Enable BLE 32K Timer */
    //BB_SetTimerEnable(1);
	BB_REG_WRITE(BB_SLEEP_TIMER1_ADDR, BB_SLEEP_TIMER1_EN_Mask, BB_SLEEP_TIMER1_EN_Pos, 1);
	sleep_us(60);//wait 32k sleep timer stable. must
	//BB_Delay(1);//wait 32k sleep timer stable.

#if CFG_SLEEP_CLOCK_SOURCE == 0 //RCL
	BB_Calibrate32kRC(32000);
#endif

    /* Set Flash SPI Clock */
    if(CLK->CLK_TOP_CTRL & CLK_SYS_SRCSEL_DPLL) {
        CLK->AHB_CLK_CTRL |= BIT(20);
    }
    else{
    	CLK->AHB_CLK_CTRL &= ~BIT(20);
    }

    /* Disable P56 wake-up source */
	ANA->LP_FL_CTRL &= ~0x3u;

	 /* Enable 32k timer wake-up source */
	ANA->LP_FL_CTRL |= 0x8u;

	/* Enable Sleep INT and Standby INT */
	ANA->LP_INT_CTRL = 0x3u;

	/* Open Sleep IRQ */
	NVIC_EnableIRQ(SLEEP_IRQn);

	/* Close Standby IRQ */
	NVIC_DisableIRQ(STANDBY_IRQn);

    SYS_LockReg();
}

void LoadCalibrationInfo(void)
{
#if CFG_HW_CALIB_ENABLE
	if(SystemHwParamLoader() == false){
		// Load FT Failed.
		TRACK_ERR("Load Calibration Info error.\r\n");
	}
	else{
		TRACK_WRN("FT Info Load Successfully:\r\n");
		TRACK("\t- Chip Version      : 0x%02x\n", otp.m.chip_ver);
		TRACK("\t- Chip FT Version   : %d\n",     otp.m.ft_version);

		uint8_t *pMac = &otp.m.mac_addr[0];
		TRACK("\t- Chip MAC Address  : %02X:%02X:%02X:%02X:%02X:%02X\r\n",pMac[5], pMac[4],pMac[3],pMac[2],pMac[1],pMac[0]);

		extern FLASH_IDS_T flash_ids;
		TRACK("\t- Chip Flash Size   : %ld KB\n", (BIT(flash_ids.memory_density_id) >> 10));
	}
#endif
}

#elif defined(IP_107)
void RCC_Wait3VSync(void)
{
    ANA->LP_REG_SYNC |= ANAC_LP_REG_SYNC_3V_Msk | ANAC_LP_REG_SYNC_3V_TRG_Msk;
    while(ANA->LP_REG_SYNC & (ANAC_LP_REG_SYNC_3V_TRG_Msk));

}

void RCC_XTHPowerOn(void)
{
	/* Enable fsyn_digclk / Enable startup fast */
	CLK->XTH_CTRL |= BIT(2)|BIT(1);

	/* Power ON XTH */
	CLK->XTH_CTRL |= BIT(0);

	/* Wait XTH stable */
	while(!(CLK->XTH_CTRL & BIT(24))){}
}

void RCC_RCHPowerOn(void)
{
	/* Power on RCH */
	CLK->RCH_CTRL |= BIT(0);

	/* Wait RCH stable */
	while(!(CLK->RCH_CTRL & BIT(24))){}
}

void RCC_RCHPowerOff(void)
{
	CLK->RCH_CTRL &= ~BIT(0);
}

void RCC_PLLPowerOn(uint8_t PLLClkSrc, uint8_t PLLOutFreq)
{
	uint32_t pllReg = CLK->DPLL_CTRL;
	pllReg &= ~(BIT(3)|BIT(2));
	CLK->DPLL_CTRL = pllReg | (PLLClkSrc << 3) | (PLLOutFreq << 2);

	/* Power on PLL */
	CLK->DPLL_CTRL |= BIT(0);

	/* Wait PLL stable */
	while(!(CLK->DPLL_CTRL & BIT(24))){}
}

void RCC_PLLPowerOff(void)
{
	/* Power on PLL */
	CLK->DPLL_CTRL &= ~BIT(0);
}

void RCC_RCLPowerOn(void)
{
	/* Power on RCL */
	CLK->RCL_CTRL_3V |= BIT(0);

	/* Wait RCL stable */
	while(!(CLK->RCL_CTRL_3V & BIT(24))){}
}

void RCC_RCLPowerOff(void)
{
	/* Power on RCL */
	CLK->RCL_CTRL_3V &= ~BIT(0);

	/* Wait RCL stable */
	//while(!(CLK->RCL_CTRL_3V & BIT(24))){}
}

void RCC_XTLPowerOn(void)
{
	/* Power on XTL */
	CLK->XTL_CTRL_3V |= BIT(0);

	/* Wait XTL stable */
	while(!(CLK->XTL_CTRL_3V & BIT(24))){}
}

void RCC_XTLPowerOff(void)
{
	/* Power on XTL */
	CLK->XTL_CTRL_3V &= ~BIT(0);

	/* Wait XTL stable */
	//while(!(CLK->XTL_CTRL_3V & BIT(24))){}
}

void RCC_SetSysClkSrc(uint8_t sysClkSrc)
{
	uint32_t reg = CLK->CLK_TOP_CTRL_3V;
	reg &= ~(3ul<<8);
	CLK->CLK_TOP_CTRL_3V = reg|(sysClkSrc<<8);
}

void RCC_Set32kClkSrc(uint8_t clk32kSrc)
{
	if(RCC_32kClkSrc_RCL == clk32kSrc){
		CLK->CLK_TOP_CTRL_3V &= ~BIT(10);
	}
	else{
		CLK->CLK_TOP_CTRL_3V |= BIT(10);
	}
}

#if CFG_VECTOR_REMAP_EN
/* !!! Must 256Bytes aligned */
ALIGN(256) uint32_t vector[64] = {0};
void InitVectorTbl(void)
{
    memcpy((uint8_t*)&vector[0], 0, 256);
    uint32_t addr = (uint32_t)&vector[0];
    addr>>=8;
    ANA->CPU_ADDR_REMAP_CTRL = 0;
    ANA->CPU_ADDR_REMAP_CTRL = addr|BIT(31);
}
#endif

void SysClkInit(void)
{
#if CFG_VECTOR_REMAP_EN
	/* Re-map vector */
	InitVectorTbl();
#endif

#if CFG_DCDC_ENABLE //DCDC
	PWR_SetDcdcModeEnable(CFG_DCDC_ENABLE);
#endif

    /* Unlock protected registers */
    SYS_UnlockReg();

    ANA->LP_FSYN_LDO |= 0x1;

    //CLK->XTH_CTRL |= BIT(3); //Enable filter TODO:
    /* Power On XTH */
    RCC_XTHPowerOn();

#if !CFG_HW_CALIB_ENABLE
  #if COB_DK_EN
	/* modify XTH cap value*/
	CLK->XTH_CTRL = (CLK->XTH_CTRL & ~CLK_XTHCTL_XO_CAP_SEL_Msk) | (0x2f << CLK_XTHCTL_XO_CAP_SEL_Pos);
  #else
	CLK->XTH_CTRL = (CLK->XTH_CTRL & ~CLK_XTHCTL_XO_CAP_SEL_Msk) | (0x10 << CLK_XTHCTL_XO_CAP_SEL_Pos);
  #endif
#endif

    /* Power On PLL */
#if CFG_SYS_CLK_MHZ == 48
	RCC_PLLPowerOn(RCC_PLLClkSrc_XTH, RCC_PLLOutFreq_48M);
#else
	RCC_PLLPowerOn(RCC_PLLClkSrc_XTH, RCC_PLLOutFreq_64M);
#endif

#if 1
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_ROM, DISABLE);//CLK_AHBPeriph_EFUSE
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_AHB|CLK_AHBPeriph_APB1|CLK_AHBPeriph_APB2|CLK_AHBPeriph_EFUSE|
    		              CLK_AHBPeriph_BLE_32K|CLK_AHBPeriph_BLE_32M, ENABLE);

    //TODO: CLK_AHBPeriph_SYSTICK can not disable.
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_GPIO|CLK_AHBPeriph_USB_AHB|CLK_AHBPeriph_USB_48M, DISABLE);

    /* save power */
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_All, DISABLE);
    CLK_APB2PeriphClockCmd(CLK_APB2Periph_All, DISABLE);
#else
    CLK_AHBPeriphClockCmd(CLK_AHBPeriph_All,   ENABLE);
    CLK_APB1PeriphClockCmd(CLK_APB1Periph_All, ENABLE);
    CLK_APB2PeriphClockCmd(CLK_APB2Periph_All, ENABLE);
#endif

    CLK_HCLKConfig(0);
    CLK_PCLK1Config(0);
    CLK_PCLK2Config(0);

    /* Select system clock source */
#if CFG_SYS_CLK_MHZ == 48 || CFG_SYS_CLK_MHZ == 64
    RCC_SetSysClkSrc(RCC_SysClkSrc_PLL);
#elif CFG_SYS_CLK_MHZ == 32
    //RCC_SetSysClkSrc(RCC_SysClkSrc_XTH);
    //RCC_PLLPowerOff(); //Disable PLL for saving power

    CLK_HCLKConfig(1); //64M/2 = 32M
    RCC_SetSysClkSrc(RCC_SysClkSrc_PLL);
#else
    RCC_SetSysClkSrc(RCC_SysClkSrc_PLL);
#endif

    RCC_RCHPowerOff(); //Disable RCH for PM

    SystemCoreClockUpdate();

	/* Select 32K clock source */
    // Active 32K setting
    if(CFG_SLEEP_CLOCK_SOURCE == 2){
		ANA->ACT_32K_CTRL |= 0x02; //select 32k clock from 32M XTH
		ANA->ACT_32K_CTRL |= 0x01; //Enable active 32k clock
    }
	// 32K XTAL/RCL setting
	else{
		RCC_Set32kClkSrc(CFG_SLEEP_CLOCK_SOURCE);

		/* Auto power on 32k RC/XTL */
		if(CFG_SLEEP_CLOCK_SOURCE)
		{
		    /* Set XTL driver current.[xxx:important] */
		    REG_WR_BITS(&(CLK->XTL_CTRL_3V), 4, (0x7ul<<4), 7);

		    /* Power On XTL */
		    RCC_XTLPowerOn();

			ANA->LP_FL_CTRL_3V |= BIT(19); //Enable XTL
			ANA->LP_FL_CTRL_3V &= ~BIT(20);//disable RCL

			RCC_RCLPowerOff();//Disable RCL for PM
		}
		else{
			/* Power On RCL */
			//RCC_RCLPowerOn(); //default power on

			ANA->LP_FL_CTRL_3V &= ~BIT(19); //disable XTL
			ANA->LP_FL_CTRL_3V |= BIT(20);  //Enable RCL
		}

		sleep_us(66);
	}

    /* Enable BLE 32K Timer */
    //BB_SetTimerEnable(1);
	BB_REG_WRITE(BB_SLEEP_TIMER1_ADDR, BB_SLEEP_TIMER1_EN_Mask, BB_SLEEP_TIMER1_EN_Pos, 1);
	sleep_us(60);//wait 32k sleep timer stable. must
	//BB_Delay(1);//wait 32k sleep timer stable.

#if(CFG_SLEEP_CLOCK_SOURCE == 0) //RCL
	BB_Calibrate32kRC(32000);
#endif

#if 0
    uint32_t tmp_reg;
    uint32_t lpldo1 = 0;
    uint32_t lpldoh = 0x1;

    tmp_reg = ANA->LP_LP_LDO_3V;
    tmp_reg &= ~ANAC_LPLDO_H_REF_TRIM_Msk_3v;
    tmp_reg |= (0x6 << ANAC_LPLDO_H_REF_TRIM_Pos_3v);
    tmp_reg = (tmp_reg & ~((0xf<<1) | (0xf << 17))) | ((lpldo1 << 1) | (lpldoh << 17));
    ANA->LP_LP_LDO_3V = tmp_reg;
#else
    REG_WR_BITS(&(ANA->LP_LP_LDO_3V), ANAC_LPLDO_H_REF_TRIM_Pos_3v, ANAC_LPLDO_H_REF_TRIM_Msk_3v, 6);
#endif

    SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
    LP_SetSleepMode(ANA, LP_MODE_SEL_SLEEP_MODE);

    ANA->LP_FL_CTRL_3V &= ~BIT(2); //set external wake-up level to 0.
    ANA->LP_FL_CTRL_3V |= ANAC_FL_SLEEP_CNT_EN_Msk;
    ANA->LP_INT_CTRL   |= ANAC_INT_SLEEP_TMR_WK_EN_Msk;

	/* Open Sleep IRQ */
	NVIC_EnableIRQ(LP_IRQn);

	//GPIO_DB->DBCTL &= ~BIT(31);

    SYS_LockReg();
}

void LoadCalibrationInfo(void)
{
#if CFG_HW_CALIB_ENABLE
	OTP_STRUCT_T otp;
	if(SystemHwParamLoader(&otp) == false){
		// Load FT Failed.
		TRACK_ERR("Load Calibration Info Failed.\r\n");
	}
	else
	{
		TRACK_WRN("FT Info Load Successfully:\r\n");
		TRACK("\t- Chip Info         : 0x%02x\n", otp.m.chip_info);
		TRACK("\t- Chip FT Version   : %d\n",     otp.m.ft_version);

		uint8_t *pMac = NULL;
		if(otp.m.ft_version >= 2){
			pMac = &otp.m_v2.mac_addr[0];
		}
		else{
			pMac = &otp.m.mac_addr[0];
		}
		TRACK("\t- Chip MAC Address  : %02X:%02X:%02X:%02X:%02X:%02X\r\n",pMac[5], pMac[4],pMac[3],pMac[2],pMac[1],pMac[0]);

		extern FLASH_IDS_T flash_ids;
		TRACK("\t- Chip Flash Size   : %ld KB\n", (BIT(flash_ids.memory_density_id) >> 10));

	#if CFG_FLASH_LDO_EN
		//ANA->LP_HP_LDO &= ~(BIT(22)|BIT(21)|BIT(20)); // Trim
	    //ANA->LP_HP_LDO |= (CONFIG_FLASH_LDO_VOL<<20); // Trim
		//ANA->LP_HP_LDO &= ~BIT(19); // 0:use internal cap, 1:use external cap. default:1
		ANA->LP_HP_LDO &= ~BIT(16); // 0:Enable Flash LDO, 1: bypass(VBAT). default:1
	#endif

	#if CFG_PM_OPTIMIZE_EN
		//buck out(DCDC):default - 2; default:8
		REG_WR_BITS(&(ANA->LP_BUCK_3V), 2, (0xFul<<2), ((otp.m.buck_out_trim >> 1) - 2));

		//HPLDO(DVDD) default - 1/2; default:8
		REG_WR_BITS(&(ANA->LP_HP_LDO), 3, (0xFul<<3), (otp.m.hp_ldo_trim - 1));   // ~1.16V
		//REG_WR_BITS(&(ANA->LP_HP_LDO), 3, (0xFul<<3), (otp.m.hp_ldo_trim - 2)); // ~1.12V
	#endif
	}
#else
  #if CFG_FLASH_LDO_EN
	/* Flash LDO support: LDO:1.8V */
    ANA->LP_HP_LDO = 0x00320231;
  #endif
#endif

#if 0
    /* From FT */
    REG_WR_BITS(&(ANA->LP_LP_LDO_3V), ANAC_LPLDO_H_REF_TRIM_Pos_3v, ANAC_LPLDO_H_REF_TRIM_Msk_3v, 6);
#endif
}
#endif

#if CFG_OS_EN
__define_initcall(SysClkInit,   0)
#endif
